/*
 * Socket wrapper functions.
 * These could all go into separate files, so only the ones needed cause
 * the corresponding function to be added to the executable.  If sockets
 * are a library (SVR4) this might make a difference (?), but if sockets
 * are in the kernel (BSD) it doesn't matter.
 *
 * These wrapper functions also use the same prototypes as POSIX.1g,
 * which might differ from many implementations (i.e., POSIX.1g specifies
 * the fourth argument to getsockopt() as "void *", not "char *").
 *
 * If your system's headers are not correct [i.e., the Solaris 2.5
 * <sys/socket.h> omits the "const" from the second argument to both
 * bind() and connect()], you'll get warnings of the form:
 * warning: passing arg 2 of `bind' discards `const' from pointer target type
 * warning: passing arg 2 of `connect' discards `const' from pointer target type
 */
#include "unp.h"
#include "SocketWrapper.h"

logging::Log* SocketWrapper::_log = logging::LogManager::getLog("socket.wrapper");

SocketWrapper::SocketWrapper() {
}

int SocketWrapper::accept(int fd, struct sockaddr *sa, socklen_t *salenptr) {
	int n;
retry:
	if ((n = ::accept(fd, sa, salenptr)) < 0) {
#ifdef	EPROTO
		if (errno == EPROTO || errno == ECONNABORTED) {
#else
		if (errno == ECONNABORTED) {
#endif
			_log->warn() << "syscall: accept aborted - retrying" << logging::end();
			goto retry;
		} else {
			_log->fatal() << "syscall: accept error" << logging::end();
			exit(1);
		}
	}
	return(n);
}

void SocketWrapper::bind(int fd, const struct sockaddr *sa, socklen_t salen) {
	if(::bind(fd, sa, salen) < 0 ) {
		_log->fatal() << "syscall: bind error " << strerror(errno) <<logging::end();
		exit(1);
	}
}

void SocketWrapper::close(int fd) {
	/*
	if (::close(fd) < 0) {
		_log->warn() << "syscall: close error " << strerror(errno) << logging::end();
	}
	*/
}

void SocketWrapper::connect(int fd, const struct sockaddr *sa, socklen_t salen) {
	if (::connect(fd, sa, salen) < 0) {
		_log->fatal() << "syscall: connect error " << strerror(errno) << logging::end();
		exit(1);
	}
}

void SocketWrapper::getPeerName(int fd, struct sockaddr *sa, socklen_t *salenptr) {
	if (::getpeername(fd, sa, salenptr) < 0) {
	_log->fatal() << "syscall: getpeername error" << strerror(errno) << logging::end();
		exit(1);
	}
}

void SocketWrapper::getSockName(int fd, struct sockaddr *sa, socklen_t *salenptr) {
	if (::getsockname(fd, sa, salenptr) < 0) {
		_log->fatal() << "syscall: getsockname error" << strerror(errno) << logging::end();
		exit(1);
	}
}

void SocketWrapper::getSockOpt(int fd, int level, int optname,
			       void *optval, socklen_t *optlenptr) {
	if (::getsockopt(fd, level, optname, optval, optlenptr) < 0) {
		_log->fatal() << "syscall: getsockopt error" << strerror(errno) << logging::end();
		exit(1);
	}
}

#ifdef	HAVE_INET6_RTH_INIT
int SocketWrapper::inet6RthSpace(int type, int segments) {
	int ret;
	ret = ::inet6_rth_space(type, segments);
	if (ret < 0) {
		_log->fatal() << "inet6_rth_space error" << logging::end();
		exit(1);
	}
	return ret;
}

void* SocketWrapper::inet6RthInit(void *rthbuf, socklen_t rthlen, int type, int segments) {
	void *ret;
	ret = ::inet6_rth_init(rthbuf, rthlen, type, segments);
	if (ret == NULL)
		_log->fatal() << "inet6_rth_init error" << logging::end();
		exit(1);
	}
	return ret;
}

void SocketWrapper::inet6RthAdd(void *rthbuf, const struct in6_addr *addr) {
	if (::inet6_rth_add(rthbuf, addr) < 0) {
		_log->fatal() << "inet6_rth_add error" << logging::end();
		exit(1);
	}
}

void SocketWrapper::inet6RthReverse(const void *in, void *out) {
	if (::inet6_rth_reverse(in, out) < 0) {
		_log->fatal() << "inet6_rth_reverse error" << logging::end();
		exit(1);
	}
}

int SocketWrapper::inet6RthSegments(const void *rthbuf) {
	int ret;

	ret = ::inet6_rth_segments(rthbuf);
	if (ret < 0) {
		_log->fatal() << "inet6_rth_segments error" << logging::end();
		exit(1);
	}
	return ret;
}

struct in6_addr* SocketWrapper::inet6RthGetAddr(const void *rthbuf, int idx) {
	struct in6_addr *ret;

	ret = ::inet6_rth_getaddr(rthbuf, idx);
	if (ret == NULL) { 
		_log->fatal() << "inet6_rth_getaddr error" << logging::end();
		exit(1);
	}

	return ret;
}
#endif

void SocketWrapper::listen(int fd, int backlog) {
	if (::listen(fd, backlog) < 0) {
		_log->fatal() << "syscall: listen error" << logging::end();
		exit(1);
	}
}

#ifdef	HAVE_POLL
int SocketWrapper::poll(struct pollfd *fdarray, unsigned long nfds, int timeout) {
	int n;
	if ( (n = ::poll(fdarray, nfds, timeout)) < 0) {
		_log->fatal() << "syscall: poll error" << logging::end();
		exit(1);
	}
	return(n);
}
#endif

ssize_t SocketWrapper::recv(int fd, void *ptr, size_t nbytes, int flags) {
	ssize_t	n;
	if ( (n = ::recv(fd, ptr, nbytes, flags)) < 0) {
		_log->fatal() << "syscall: recv error" << logging::end();
		exit(1);
	}
	return(n);
}

ssize_t SocketWrapper::recvFrom(int fd, void *ptr, size_t nbytes, int flags,
				struct sockaddr *sa, socklen_t *salenptr) {
	ssize_t	n;
	if ((n = ::recvfrom(fd, ptr, nbytes, flags, sa, salenptr)) < 0) {
		_log->fatal() << "syscall: recvfrom error" << logging::end();
		exit(1);
	}
	return(n);
}

ssize_t SocketWrapper::recvMessage(int fd, struct msghdr *msg, int flags) {
	ssize_t	n;

	if ( (n = ::recvmsg(fd, msg, flags)) < 0) {
		_log->fatal() << "syscall: recvmsg error" << logging::end();
		exit(1);
	}
	return(n);
}

int SocketWrapper::select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
			  struct timeval *timeout) {
	int n;

	if ( (n = ::select(nfds, readfds, writefds, exceptfds, timeout)) < 0) {
		_log->fatal() << "syscall: select error" << logging::end();
		exit(1);
	}
	return(n);		// can return 0 on timeout
}

void SocketWrapper::send(int fd, const void *ptr, size_t nbytes, int flags) {
	if (::send(fd, ptr, nbytes, flags) != (ssize_t)nbytes) {
		_log->fatal() << "syscall: send error" << logging::end();
		exit(1);
	}
}

void SocketWrapper::sendTo(int fd, const void *ptr, size_t nbytes, int flags,
			   const struct sockaddr *sa, socklen_t salen) {
	if (::sendto(fd, ptr, nbytes, flags, sa, salen) != (ssize_t)nbytes) {
		_log->fatal() << "syscall: sendto error" << logging::end();
		exit(1);
	}
}

void SocketWrapper::sendMessage(int fd, const struct msghdr *msg, int flags) {
	unsigned int i;
	ssize_t	nbytes;

	nbytes = 0;	// must first figure out what return value should be 
	for (i = 0; i < msg->msg_iovlen; i++)
		nbytes += msg->msg_iov[i].iov_len;

	if (::sendmsg(fd, msg, flags) != nbytes) {
		_log->fatal() << "syscall: sendmsg error" << logging::end();
		exit(1);
	}
}

void SocketWrapper::setSockOpt(int fd, int level, int optname, 
			       const void *optval, socklen_t optlen) {
	if (::setsockopt(fd, level, optname, optval, optlen) < 0) {
		_log->fatal() << "syscall: setsockopt error" << logging::end();
		exit(1);
	}
}

void SocketWrapper::shutdown(int fd, int how) {
	if (::shutdown(fd, how) < 0) {
		_log->fatal() << "syscall: shutdown error" << logging::end();
		exit(1);
	}
}

int SocketWrapper::sockAtMark(int fd) {
	int n;
	if ( (n = ::sockatmark(fd)) < 0) {
		_log->fatal() << "syscall: sockatmark error" << logging::end();
		exit(1);
	}
	return(n);
}

int SocketWrapper::socket(int family, int type, int protocol) {
	int n;
	if ( (n = ::socket(family, type, protocol)) < 0) {
		_log->fatal() << "syscall: socket error" << logging::end();
		exit(1);
	}
	return(n);
}

void SocketWrapper::socketPair(int family, int type, int protocol, int *fd) {
	int n;
	if ( (n = ::socketpair(family, type, protocol, fd)) < 0) {
		_log->fatal() << "syscall: socketpair error" << logging::end();
		exit(1);
	}
}
